#include "dev_touch.h"

#define TOUCH_I2C_BUS_NAME      "i2c4"            /* 传感器连接的I2C总线设备名称 */
#define TOUCH_I2C_SLAVE_ADDR1    (0xBA >> 1)      /* 从机地址1 (管脚时序设定) (在rt-thread驱动中, 会自动 << 1) */
#define TOUCH_I2C_SLAVE_ADDR2    (0x28 >> 1)      /* 从机地址2 (管脚时序设定) (在rt-thread驱动中, 会自动 << 1) */

/* touch 设备管理结构体 */
static dev_rtdrv_touch_t s_dev_rtdrv_touch;

/* 
 * [0] = 0x41 => 'A'
 * <x, y> 坐标最大输出值[不可]在该数组中设置(通过修改宏完成设置)
 */
static uint8_t s_gt911_cfg_params[] =
{
    0x41, 0x00, 0x04, 0x58, 0x02, 0x05, 0x0D, 0x00, 0x02, 0x0F, 
    0x28, 0x0F, 0x50, 0x32, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x29, 0x0A, 
    0x2D, 0x2F, 0x0F, 0x0A, 0x00, 0x00, 0x00, 0x02, 0x02, 0x1D, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x1E, 0x50, 0x94, 0xC5, 0x02, 0x07, 0x00, 0x00, 0x04, 
    0xA3, 0x21, 0x00, 0x8C, 0x28, 0x00, 0x78, 0x31, 0x00, 0x69, 
    0x3B, 0x00, 0x5D, 0x48, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 
    0x12, 0x14, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x1D, 
    0x1E, 0x1F, 0x20, 0x21, 0x22, 0x24, 0x26, 0x28, 0xFF, 0xFF, 
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x00, 0x00, 0xBF, 0x01
};

/* 初始化其他管脚 */
static void s_dev_touch_gpio_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStruct = {0};

    BSP_DEV_TOUCH_PIN_RCC_ENABLE;       /* 时钟使能 */

    GPIO_InitStruct.Pin   = BSP_DEV_TOUCH_RST_PIN;
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;      /* 设为输出口 */
    GPIO_InitStruct.Pull  = GPIO_NOPULL;              /* 上下拉电阻不使能 */
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
    HAL_GPIO_Init(BSP_DEV_TOUCH_RST_GPIO, &GPIO_InitStruct);

    GPIO_InitStruct.Pin   = BSP_DEV_TOUCH_INT_PIN;
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;      /* 设为输出口 */
    GPIO_InitStruct.Pull  = GPIO_NOPULL;         /* 上下拉电阻不使能 */
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
    HAL_GPIO_Init(BSP_DEV_TOUCH_INT_GPIO, &GPIO_InitStruct);

    return;
}

/* 根据宏定义配置, 初始化通知(INT)管脚 */
static void s_dev_touch_notice_pin_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStruct = {0};

    BSP_DEV_TOUCH_PIN_RCC_ENABLE;       /* 时钟使能 */

#if (DEV_TOUCH_NOTICE_MODE == DEV_TOUCH_NOTICE_REDGE)       /* 上升沿触发 */

    GPIO_InitStruct.Pin  = BSP_DEV_TOUCH_INT_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(BSP_DEV_TOUCH_INT_GPIO, &GPIO_InitStruct);

    HAL_NVIC_SetPriority(BSP_DEV_TOUCH_INT_IRQ_TYPE, BSP_DEV_TOUCH_INT_IRQ_PRI); /* 抢占优先级，子优先级为 */
    HAL_NVIC_EnableIRQ(BSP_DEV_TOUCH_INT_IRQ_TYPE);              /* 使能中断线 */

#elif (DEV_TOUCH_NOTICE_MODE == DEV_TOUCH_NOTICE_DEDGE)     /* 下降沿触发 */

    GPIO_InitStruct.Pin  = BSP_DEV_TOUCH_INT_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(BSP_DEV_TOUCH_INT_GPIO, &GPIO_InitStruct);

    HAL_NVIC_SetPriority(BSP_DEV_TOUCH_INT_IRQ_TYPE, BSP_DEV_TOUCH_INT_IRQ_PRI); /* 抢占优先级，子优先级 */
    HAL_NVIC_EnableIRQ(BSP_DEV_TOUCH_INT_IRQ_TYPE);              /* 使能中断线 */

#elif ((DEV_TOUCH_NOTICE_MODE == DEV_TOUCH_NOTICE_LPOLL))    /* 低电平查询 */

    GPIO_InitStruct.Pin   = BSP_DEV_TOUCH_INT_PIN;
    GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;      /* 设为输出口 */
    GPIO_InitStruct.Pull  = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;

    HAL_GPIO_Init(BSP_DEV_TOUCH_INT_GPIO, &GPIO_InitStruct);

#elif ((DEV_TOUCH_NOTICE_MODE == DEV_TOUCH_NOTICE_HPOLL))    /* 高电平查询 */

    GPIO_InitStruct.Pin   = BSP_DEV_TOUCH_INT_PIN;
    GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;      /* 设为输出口 */
    GPIO_InitStruct.Pull  = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;

    HAL_GPIO_Init(BSP_DEV_TOUCH_INT_GPIO, &GPIO_InitStruct);

#endif

    /* 标记触摸状态为 NONE */
    s_dev_rtdrv_touch->touch_status = DEV_TOUCH_PRESS_NONE;

    return;
}

/* 控制复位管脚 */
#define S_DEV_TOUCH_RST_CTRL(state)    (state == 1) ?                                                   \
                                       HAL_GPIO_WritePin(BSP_DEV_TOUCH_RST_GPIO, BSP_DEV_TOUCH_RST_PIN, \
                                                         GPIO_PIN_SET) :                                \
                                       HAL_GPIO_WritePin(BSP_DEV_TOUCH_RST_GPIO, BSP_DEV_TOUCH_RST_PIN, \
                                                         GPIO_PIN_RESET)                                \
                                    

/* 控制中断管脚 */
#define S_DEV_TOUCH_INT_CTRL(state)    (state == 1) ?                                                   \
                                       HAL_GPIO_WritePin(BSP_DEV_TOUCH_INT_GPIO, BSP_DEV_TOUCH_INT_PIN, \
                                                         GPIO_PIN_SET) :                                \
                                       HAL_GPIO_WritePin(BSP_DEV_TOUCH_INT_GPIO, BSP_DEV_TOUCH_INT_PIN, \
                                                         GPIO_PIN_RESET)                                \

/* 控制时序, 设置从机地址 */
static void s_dev_touch_reset_sequence(uint8_t slave_addr)
{
    /* 由RST和INT管脚设定从机地址 (0xBA) */
    if (slave_addr == TOUCH_I2C_SLAVE_ADDR1)
    {
        S_DEV_TOUCH_RST_CTRL(0);
        S_DEV_TOUCH_INT_CTRL(0);
        soc_time_delay_ms(10);
        S_DEV_TOUCH_RST_CTRL(1);
        S_DEV_TOUCH_INT_CTRL(0);
        soc_time_delay_ms(10);

        /* 标记从机地址 */
        s_dev_rtdrv_touch->slave_addr = TOUCH_I2C_SLAVE_ADDR1;
    }
    else if (slave_addr == TOUCH_I2C_SLAVE_ADDR2)    /* (0x28) */
    {
        S_DEV_TOUCH_RST_CTRL(0);
        S_DEV_TOUCH_INT_CTRL(1);
        soc_time_delay_ms(10);
        S_DEV_TOUCH_RST_CTRL(1);
        S_DEV_TOUCH_INT_CTRL(1);
        soc_time_delay_ms(10);

        /* 标记从机地址 */
        s_dev_rtdrv_touch->slave_addr = TOUCH_I2C_SLAVE_ADDR2;
    }

    return;
}

/* 找到i2c控制总线 */
static RET_STATUS s_dev_touch_find_i2c_bus(dev_rtdrv_touch_t rtdrv_touch)
{
    const char *i2c_bus_name = TOUCH_I2C_BUS_NAME;

    rtdrv_touch->i2c_bus = (struct rt_i2c_bus_device *) rt_device_find(TOUCH_I2C_BUS_NAME);

    if (rtdrv_touch->i2c_bus == RT_NULL)
    {
        rt_kprintf("can't find %s device!\n", i2c_bus_name);

        return RET_ERROR;
    }

    return RET_OK;
}

/* 写寄存器 */
static RET_STATUS s_dev_touch_write_regs(uint8_t *regs, uint8_t *data_buf, uint16_t len)
{
    struct rt_i2c_msg msgs;
    uint8_t           merge_buf[0xFF] = {0};    /* 最大申请0xFF 字节的数组空间 */

    /* 合并数据 */
    memcpy(&merge_buf[0], regs, DEV_TOUCH_REG_NOB);          /* 目标寄存器地址部分字节 */
    memcpy(&merge_buf[0 + DEV_TOUCH_REG_NOB], data_buf, len);    /* 往目标寄存器地址写入数据部分字节 */

    /* 构建数据包, 发送数据 */
    msgs.addr  = s_dev_rtdrv_touch->slave_addr;
    msgs.flags = RT_I2C_WR;
    msgs.buf   = merge_buf; /* 发送合并后的数组数据 */
    msgs.len   = DEV_TOUCH_REG_NOB + len;

    if (rt_i2c_transfer(s_dev_rtdrv_touch->i2c_bus, &msgs, 1) != 1)
    {
        return RET_ERROR;   /* 传输失败 */
    }

    return RET_OK;
}

/* 读寄存器 */
static RET_STATUS s_dev_touch_read_regs(uint8_t *regs, uint8_t *data_buf, uint16_t len)
{
    struct rt_i2c_msg msgs[2];

    /* 构建数据包, 发送数据 */
    msgs[0].addr  = s_dev_rtdrv_touch->slave_addr;
    msgs[0].flags = RT_I2C_WR;
    msgs[0].buf   = regs;
    msgs[0].len   = DEV_TOUCH_REG_NOB;  /* gt911的寄存器是2字节 */

    msgs[1].addr  = s_dev_rtdrv_touch->slave_addr;
    msgs[1].flags = RT_I2C_RD;
    msgs[1].buf   = data_buf;
    msgs[1].len   = len;

    if (rt_i2c_transfer(s_dev_rtdrv_touch->i2c_bus, msgs, 2) != 2)
    {
        return RET_ERROR;   /* 传输失败 */
    }

    return RET_OK;
}

/* 触摸屏寄存器配置 */
static RET_STATUS s_dev_touch_reg_config(void)
{
    uint8_t  reg[2] = {0}, fix_version = 0, cfg_version = 0, check_sum = 0;
    uint16_t i;
    uint16_t total_len, valid_len, check_offset;

    /* 校验设备管理结构体参数有效性 */
    if (s_dev_rtdrv_touch == NULL)
    {
        return RET_ERROR;
    }

    /* 指向配置参数数组 */
    s_dev_rtdrv_touch->cfg_params = s_gt911_cfg_params;
    s_dev_rtdrv_touch->cfg_size   = sizeof(s_gt911_cfg_params);

    /* 计算和配置参数表相关的数据 */
    total_len    = s_dev_rtdrv_touch->cfg_size; /* 参数表总的字节数 */
    valid_len    = total_len - 2;               /* 去掉最后两个字节 */
    check_offset = valid_len;                   /* valid部分: [0]~[valid_len - 1] */

    /* 存储配置数组中的固定版本数据 */
    fix_version = s_dev_rtdrv_touch->cfg_params[0];

    /* 读取配置版本 */
    reg[0] = (DEV_TOUCH_CONFIG_VERSION_REG & 0xFF00) >> 8;
    reg[1] = DEV_TOUCH_CONFIG_VERSION_REG & 0xFF;

    if (s_dev_touch_read_regs(reg, &cfg_version, 1) != RET_OK)
    {
        return RET_ERROR;   /* 读取失败, 返回错误 */
    }

    /* 配置版本数据 */
#if (DEV_TOUCH_CFG_VER_CLEAR_ACTION == DEV_TOUCH_CFG_VER_CLEAR_ENABLE)
    s_dev_rtdrv_touch->cfg_params[0] = 0x00;    /* 清空配置版本寄存器 (reset to 'A') */
#else
    s_dev_rtdrv_touch->cfg_params[0] = (cfg_version > fix_version) ? cfg_version : fix_version;
#endif
    
    /* 配置X和Y方向的触摸点坐标最大值 */
    s_dev_rtdrv_touch->cfg_params[1] = (DEV_TOUCH_MAX_X_RANGE & 0xFF);
    s_dev_rtdrv_touch->cfg_params[2] = (DEV_TOUCH_MAX_X_RANGE & 0xFF00) >> 8;
    s_dev_rtdrv_touch->cfg_params[3] = (DEV_TOUCH_MAX_Y_RANGE & 0xFF);
    s_dev_rtdrv_touch->cfg_params[4] = (DEV_TOUCH_MAX_Y_RANGE & 0xFF00) >> 8;

    /* 配置最大支持触摸点个数 */
    s_dev_rtdrv_touch->cfg_params[5] = (DEV_TOUCH_MAX_POINT_NBR & 0x0F);

    /* X, Y轴数据是否交换 (涉及横屏和竖屏) */
#if (DEV_TOUCH_X2Y_ACTION == DEV_TOUCH_X2Y_ENABLE)
    /* wait...... */
#endif

    /* 配置触摸后通知方式 */
    s_dev_rtdrv_touch->cfg_params[6] &= 0xFC;   /* 清除相关bit位 */

#if (DEV_TOUCH_NOTICE_MODE == DEV_TOUCH_NOTICE_REDGE)       /* 上升沿触发 */
    s_dev_rtdrv_touch->cfg_params[6] |= 0x00;
#elif (DEV_TOUCH_NOTICE_MODE == DEV_TOUCH_NOTICE_DEDGE)     /* 下降沿触发 */
    s_dev_rtdrv_touch->cfg_params[6] |= 0x01;
#elif (DEV_TOUCH_NOTICE_MODE == DEV_TOUCH_NOTICE_LPOLL)     /* 低电平查询 */
    s_dev_rtdrv_touch->cfg_params[6] |= 0x02;
#elif (DEV_TOUCH_NOTICE_MODE == DEV_TOUCH_NOTICE_HPOLL)     /* 高电平查询 */
    s_dev_rtdrv_touch->cfg_params[6] |= 0x03;
#endif

    /* 计算所有配置参数的校验和 */
    for (i = 0; i < valid_len; i++)
    {
        check_sum += s_dev_rtdrv_touch->cfg_params[i];
    }

    /* 获得最后两个字节的值 */
    s_dev_rtdrv_touch->cfg_params[check_offset]     = (uint8_t) (~check_sum) + 1;  /* 校验字节 (0x8047 到 0x80FE 之字节和的补码) */
    s_dev_rtdrv_touch->cfg_params[check_offset + 1] = 1;                 /* 标记配置已更新 */

    /* 写入配置数据 */
    reg[0] = (DEV_TOUCH_CONFIG_VERSION_REG & 0xFF00) >> 8;
    reg[1] = DEV_TOUCH_CONFIG_VERSION_REG & 0xFF;

    if (s_dev_touch_write_regs(reg, s_dev_rtdrv_touch->cfg_params, total_len) != RET_OK)
    {
        return RET_ERROR;   /* 写入失败 */
    }

    /* 写入成功 */
    return RET_OK;
}

/* 进入读坐标状态 */
static RET_STATUS s_dev_touch_start_work(void)
{
    uint8_t reg[2] = {0};
    uint8_t buf[1] = {0};
    
    /* 发送软件复位指令 */
    reg[0] = (DEV_TOUCH_CMD_CTRL_REG & 0xFF00) >> 8;
    reg[1] = (DEV_TOUCH_CMD_CTRL_REG & 0xFF);
    
    buf[0] = DEV_TOUCH_CMD_READ_COORD;
    
    if (s_dev_touch_write_regs(reg, buf, 1) != RET_OK)
    {
        return RET_ERROR;
    }
    
    return RET_OK;
}

/* 清除触摸屏数据就绪标志位 */
static RET_STATUS s_dev_touch_reset_df_status(void)
{
    uint8_t reg[2] = {0};
    uint8_t buf[1] = {0};
    
    /* 发送清除数据状态指令 */
    reg[0] = (DEV_TOUCH_POINTS_STATUS_REG & 0xFF00) >> 8;
    reg[1] = (DEV_TOUCH_POINTS_STATUS_REG & 0xFF);
    
    buf[0] = 0;
    
    if (s_dev_touch_write_regs(reg, buf, 1) != RET_OK)
    {
        return RET_ERROR;
    }
    
    return RET_OK;
}

/* 获得寄存器配置信息(测试用) */
static RET_STATUS s_dev_touch_get_cfg(void)
{
    uint8_t i;
    uint8_t reg[2]                        = {0};
    uint8_t buf[DEV_TOUCH_ALL_CFGREG_NBR] = {0};


    /* 读取 所有寄存器配置信息 */
    reg[0] = (DEV_TOUCH_CONFIG_VERSION_REG & 0xFF00) >> 8;
    reg[1] = (DEV_TOUCH_CONFIG_VERSION_REG & 0xFF);

    if (s_dev_touch_read_regs(reg, buf, DEV_TOUCH_ALL_CFGREG_NBR) != RET_OK)
    {
        return RET_ERROR; /*读取失败*/
    }

    rt_kprintf("\r\n");
    for (i = 0; i < DEV_TOUCH_ALL_CFGREG_NBR; i++)
    {
        rt_kprintf("0x%.2x,", buf[i]);
    }
    rt_kprintf("\r\n");

    return RET_OK;
}

/* 触摸屏信息获取 */
static RET_STATUS s_dev_touch_get_info(void)
{
    uint8_t buf[4] = {0};
    uint8_t reg[2] = {0};

    /* 获取触摸屏最大支持的触摸点值 */
    reg[0] = (DEV_TOUCH_X_OUTPUT_MAX_REG & 0xFF00) >> 8;
    reg[1] = DEV_TOUCH_X_OUTPUT_MAX_REG & 0xFF;

    if (s_dev_touch_read_regs(reg, buf, 4) != RET_OK)
    {
        return RET_ERROR;
    }

    s_dev_rtdrv_touch->coord_max.x = ((uint16_t) buf[1] << 8) | buf[0];
    s_dev_rtdrv_touch->coord_max.y = ((uint16_t) buf[3] << 8) | buf[2];

    /* 获取触摸屏的产品ID */
    reg[0] = (DEV_TOUCH_PRODUCT_ID_REG & 0xFF00) >> 8;
    reg[1] = DEV_TOUCH_PRODUCT_ID_REG & 0xFF;

    if (s_dev_touch_read_regs(reg, buf, 4) != RET_OK)
    {
        return RET_ERROR;
    }

    memcpy(s_dev_rtdrv_touch->product_id, buf, 4);  /* 产品ID是字符串 */

    /* 获取触摸屏的固件版本 */
    reg[0] = (DEV_TOUCH_FIRMWARE_VERSION_REG & 0xFF00) >> 8;
    reg[1] = DEV_TOUCH_FIRMWARE_VERSION_REG & 0xFF;

    if (s_dev_touch_read_regs(reg, buf, 2) != RET_OK)
    {
        return RET_ERROR;
    }

    s_dev_rtdrv_touch->firmware_version = ((uint16_t) buf[1] << 8) | buf[0];

    /* 获取触摸屏的配置版本 */
    reg[0] = (DEV_TOUCH_CONFIG_VERSION_REG & 0xFF00) >> 8;
    reg[1] = DEV_TOUCH_CONFIG_VERSION_REG & 0xFF;

    if (s_dev_touch_read_regs(reg, buf, 1) != RET_OK)
    {
        return RET_ERROR;
    }

    s_dev_rtdrv_touch->config_version = buf[0];

    /* 获取完毕 */
    return RET_OK;
}

/* 触摸设备初始化 */
RET_STATUS dev_touch_init(void)
{
    /* 分配设备管理结构体 */
    s_dev_rtdrv_touch = (dev_rtdrv_touch_t) rt_calloc(1, sizeof(dev_rtdrv_touch));

    if (s_dev_rtdrv_touch == NULL)
    {
        return RET_ERROR;
    }

    /* 找到i2c控制总线 */
    if (s_dev_touch_find_i2c_bus(s_dev_rtdrv_touch) != RET_OK)
    {
        goto fail_handler;
    }

    /* 初始化其他管脚 */
    s_dev_touch_gpio_init();

    /* 控制时序, 设置从机地址 */
    s_dev_touch_reset_sequence(TOUCH_I2C_SLAVE_ADDR1);

    /* 初始化通知管脚 */
    s_dev_touch_notice_pin_init();

    /* 软件复位 */
    dev_touch_soft_reset();

    /* 延时等待重启(必须) */
    soc_time_delay_ms(DEV_TOUCH_WAIT_RESTART_TIME_MS);

    /* 寄存器配置 */
#if (DEV_TOUCH_CFG_REG_WR_ACTION == DEV_TOUCH_CFG_REG_WR_ENABLE)
    if (s_dev_touch_reg_config() != RET_OK)
    {
        goto fail_handler;
    }
#endif

    /* 软件复位 */
    dev_touch_soft_reset();

    /* 延时等待校准成功 */
    soc_time_delay_ms(DEV_TOUCH_WAIT_RESTART_TIME_MS);

    /* 是否需要打印所有寄存器(186个)配置数据 */
#if (DEV_TOUCH_ALL_CFGREG_PRINT_ACTION == DEV_TOUCH_ALL_CFGREG_PRINT_ENABLE)
    s_dev_touch_get_cfg();
#endif

    /* 触摸屏信息获取 */
    if (s_dev_touch_get_info() != RET_OK)
    {
        goto fail_handler;
    }

    /* 显示触摸设备信息 */
#if (DEV_TOUCH_SHOW_INFO_ACTION == DEV_TOUCH_SHOW_INFO_ENABLE)
    dev_touch_show_info();
#endif

    /* 清除触摸屏数据就绪标志位 */
    if (s_dev_touch_reset_df_status() != RET_OK)
    {
        goto fail_handler;
    }
    
    /* 进入读坐标状态 */
    s_dev_touch_start_work();

    /* 初始化成功 */
    s_dev_rtdrv_touch->init_status = INIT_SUCC;
    return RET_OK;

    /* 初始化失败 */
fail_handler:
    rt_free(s_dev_rtdrv_touch); /* 释放分配到的内存 */
    s_dev_rtdrv_touch->init_status = INIT_FAIL;
    return RET_ERROR;
}

/* 显示触摸设备信息 */
void dev_touch_show_info(void)
{
    /* 判断设备管理指针有效性 */
    if (s_dev_rtdrv_touch == NULL)
    {
        rt_kprintf("s_dev_rtdrv_touch is NULL\r\n");
        return;
    }

    /* 判断设备是否完成初始化 */
    if (s_dev_rtdrv_touch->init_status != INIT_SUCC)
    {
        rt_kprintf("touch is uninitialized\r\n");
        return;
    }

    /* 输出设备信息(数据来自设备寄存器读出值) */
    rt_kprintf("TOUCH INFO: \r\n");
    rt_kprintf("max-x = %d, max-y = %d\r\n", s_dev_rtdrv_touch->coord_max.x, s_dev_rtdrv_touch->coord_max.y);
    rt_kprintf("product id = %s\r\n", s_dev_rtdrv_touch->product_id);
    rt_kprintf("firmware version = %d\r\n", s_dev_rtdrv_touch->firmware_version);
    rt_kprintf("config version = %c\r\n", s_dev_rtdrv_touch->config_version);

    return;
}

/* 软件复位 */
void dev_touch_soft_reset(void)
{
    uint8_t reg[2] = {0};
    uint8_t buf[1] = {0};
    
    /* 发送软件复位指令 */
    reg[0] = (DEV_TOUCH_CMD_CTRL_REG & 0xFF00) >> 8;
    reg[1] = (DEV_TOUCH_CMD_CTRL_REG & 0xFF);
    
    buf[0] = DEV_TOUCH_CMD_SOFT_RESET;
    
    if (s_dev_touch_write_regs(reg, buf, 1) != RET_OK)
    {
        return;
    }
    
    return;
}

/* 设置触摸设备触摸状态 (中断回调函数或者管脚电平轮询里调用该函数) */
void dev_touch_set_press_status(uint8_t set_status)
{
    s_dev_rtdrv_touch->touch_status = set_status;

    return;
}

/* 复位触摸设备触摸状态 */
void dev_touch_reset_press_status(void)
{
    /* 清除触摸屏数据就绪标志位 */
    s_dev_touch_reset_df_status();

    return;
}

/* 获得触摸设备触摸状态 */
uint8_t dev_touch_get_press_status(void)
{
    /* 判断触摸设备初始化状态 */
    if (s_dev_rtdrv_touch->init_status != INIT_SUCC)
    {
        dev_touch_set_press_status(DEV_TOUCH_PRESS_NONE);
    }

    /* 复位坐标数据状态机 */
    dev_touch_set_press_status(DEV_TOUCH_PRESS_NONE);
    
    /* 轮询状态在该函数内设置并获取, 中断在中断服务函数内设置 */
#if 0
#if (DEV_TOUCH_NOTICE_MODE == DEV_TOUCH_NOTICE_LPOLL)
    if (HAL_GPIO_ReadPin(BSP_DEV_TOUCH_INT_GPIO, BSP_DEV_TOUCH_INT_PIN) == GPIO_PIN_RESET)
    {
        dev_touch_set_press_status(DEV_TOUCH_PRESS_OCCUR);
    }
#elif (DEV_TOUCH_NOTICE_MODE == DEV_TOUCH_NOTICE_HPOLL)
    if (HAL_GPIO_ReadPin(BSP_DEV_TOUCH_INT_GPIO, BSP_DEV_TOUCH_INT_PIN) == GPIO_PIN_SET)
    {
        dev_touch_set_press_status(DEV_TOUCH_PRESS_OCCUR);
    }
#endif

#else   /* 测试发现通知管脚有异常, 所以改为读取寄存器 */

    uint8_t reg[2]    = {0};
    uint8_t data      = 0;
    uint8_t flag      = 0;
    uint8_t coord_nbr = 0;

    /* 读取触摸状态寄存器 */
    reg[0] = (DEV_TOUCH_POINTS_STATUS_REG & 0xFF00) >> 8;
    reg[1] = (DEV_TOUCH_POINTS_STATUS_REG & 0xFF);
    
    if (s_dev_touch_read_regs(reg, &data, 1) != RET_OK)
    {
        return DEV_TOUCH_PRESS_NONE;     /* 通信失败, 返回NONE */
    }

    /* 判断是否有数据就绪 (双重判断才有效) */
    flag      = (data & 0x80) >> 7;
    coord_nbr = data & 0x0F;

    /* 标志位有效 */
    if (flag == 1)
    {
        /* 触摸点个数有效 */
        if ((0 < coord_nbr) && (coord_nbr <= DEV_TOUCH_MAX_POINT_NBR))
        {
            dev_touch_set_press_status(DEV_TOUCH_PRESS_OCCUR);  /* 设置有触摸数据状态 */
        }
        else        /* 触摸点个数无效 */
        {
            dev_touch_set_press_status(DEV_TOUCH_PRESS_ABNOR);  /* 触摸状态异常 */
        }
    }

#endif

    return s_dev_rtdrv_touch->touch_status;             /* NONE */
}

/* 扫描触摸点, 获得触摸点信息 */
void dev_touch_scan_points(dt_coord *coords, uint8_t *coords_nbr)
{
    uint8_t i, offset = 0;
    uint8_t reg[2]  = {0};
    uint8_t buf[40] = {0};
    uint8_t point_nbr = 0, data_status = 0;

    /* 判断参数有效性 */
    if ((coords == NULL) || (coords_nbr == NULL))
    {
        *coords_nbr = 0;
        return;
    }

    /* 判断触摸设备初始化状态 */
    if (s_dev_rtdrv_touch->init_status != INIT_SUCC)
    {
        *coords_nbr = 0;
        return; /* 初始化失败, 禁止使用设备 */
    }

    /* 读取触摸状态寄存器 */
    reg[0] = (DEV_TOUCH_POINTS_STATUS_REG & 0xFF00) >> 8;
    reg[1] = (DEV_TOUCH_POINTS_STATUS_REG & 0xFF);

    if (s_dev_touch_read_regs(reg, buf, 1) != RET_OK)
    {
        *coords_nbr = 0;
        return;
    }

    /* 获得data_buf状态 和 触摸点个数值 */
    data_status = (buf[0] & 0x80) >> 7;
    point_nbr   = (buf[0] & 0x0F);

    /* 判断有触摸数据就绪(就绪后, 必须清理状态标志位) */
    if (data_status == 0)
    {
        *coords_nbr = 0;
        return; /* 数据未就绪 */
    }

    /* 判断触摸点个数有效性 */
    if ((point_nbr == 0) || (point_nbr > DEV_TOUCH_MAX_POINT_NBR))
    {
        goto exit_handler; /*错误处理*/
    }
    
    /* 读取 point_nbr 个触摸点的数据 */
    reg[0] = (DEV_TOUCH_POINT1_INFO_REG & 0xFF00) >> 8;
    reg[1] = (DEV_TOUCH_POINT1_INFO_REG & 0xFF);

    if (s_dev_touch_read_regs(reg, buf, point_nbr * DEV_TOUCH_POINTS_INFO_SIZE) != RET_OK)
    {
        goto exit_handler; /*错误处理*/
    }

    /* 获得触摸点的坐标信息 */
    for (i = 0; i < point_nbr; i++)
    {
        offset = 8 * i;
        s_dev_rtdrv_touch->points[i].track_id = buf[8 * i]; /* 触摸点跟踪id */
        s_dev_rtdrv_touch->points[i].coord.x  = ((uint16_t) buf[offset + 2] << 8) | buf[offset + 1];
        s_dev_rtdrv_touch->points[i].coord.y  = ((uint16_t) buf[offset + 4] << 8) | buf[offset + 3];
    }

    /* 记录触摸点数据 */
    for (i = 0; i < point_nbr; i++)
    {
        coords[i].x = s_dev_rtdrv_touch->points[i].coord.x;
        coords[i].y = s_dev_rtdrv_touch->points[i].coord.y;

        /* 使能触摸点调试模式将输出所有触摸点的坐标数据 */
#if (DEV_TOUCH_POINTS_DEBUG_ACTION == DEV_TOUCH_POINTS_DEBUG_ENABLE)
        rt_kprintf("id = %d, x = %d, y = %d\r\n", i, coords[i].x, coords[i].y);
#endif
    }

    /* 退出处理, 清理寄存器中的数据状态 */
exit_handler:
    *coords_nbr = point_nbr;    /* 可能为0, 可能为有效值 */
    
    /* 清除触摸屏数据就绪标志位 */
    s_dev_touch_reset_df_status();

    return;
}
